/*
 * Copyright the original author or authors.
 */
package com.derbysoft.core.support;

import org.apache.commons.lang.StringUtils;
import org.hibernate.*;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.type.Type;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

public class CountableQuery implements  Query {

    protected Query query;

    private Query countQuery;

    public Query getCountQuery() {
        return countQuery;
    }


    private HqlSyntaxAnalyzer analyzer;

    public CountableQuery(Session session, Query query) {
        String hql = query.getQueryString();
        this.query = query;
        if(session != null){
            analyzer = new HqlSyntaxAnalyzer(hql);
            if (!analyzer.isUpdateSentence()) {
                this.countQuery = session.createQuery(createCountHql(hql));
            }
        }
    }

    private String createCountHql(String hql) {
        int primaryFromIndex = analyzer.getPrimaryFromIndex();
        String prefix = "select count(*) ";
        int orderbyIndex = analyzer.getPrimaryOrderbyIndex();
        if (orderbyIndex != -1) {
            return prefix + StringUtils.substring(hql, primaryFromIndex, orderbyIndex);
        } else {
            return prefix + StringUtils.substring(hql, primaryFromIndex);
        }
    }


    @Override
    public int executeUpdate(){
        return query.executeUpdate();
    }

    @Override
    public String[] getNamedParameters(){
        return query.getNamedParameters();
    }

    @Override
    public String getQueryString() {
        return query.getQueryString();
    }

    @Override
    public String[] getReturnAliases(){
        return query.getReturnAliases();
    }

    @Override
    public Type[] getReturnTypes(){
        return query.getReturnTypes();
    }

    @Override
    public Iterator<?> iterate(){
        return query.iterate();
    }

    @Override
    public List list(){
        return query.list();
    }

    @Override
    public ScrollableResults scroll(){
        return query.scroll();
    }

    @Override
    public ScrollableResults scroll(ScrollMode scrollMode)
           {
        return query.scroll(scrollMode);
    }

    @Override
    public Query setBigDecimal(int position, BigDecimal number) {
        if (countQuery != null) {
            countQuery.setBigDecimal(position, number);
        }
        query.setBigDecimal(position, number);
        return this;
    }

    @Override
    public Query setBigDecimal(String name, BigDecimal number) {
        if (countQuery != null) {
            countQuery.setBigDecimal(name, number);
        }
        query.setBigDecimal(name, number);
        return this;
    }

    @Override
    public Query setBigInteger(int position, BigInteger number) {
        query.setBigInteger(position, number);
        return this;
    }

    @Override
    public Query setBigInteger(String name, BigInteger number) {
        if (countQuery != null) {
            countQuery.setBigInteger(name, number);
        }
        query.setBigInteger(name, number);
        return this;
    }

    @Override
    public Query setBinary(int position, byte[] val) {
        if (countQuery != null) {
            countQuery.setBinary(position, val);
        }
        query.setBinary(position, val);
        return this;
    }

    @Override
    public Query setBinary(String name, byte[] val) {
        if (countQuery != null) {
            countQuery.setBinary(name, val);
        }
        query.setBinary(name, val);
        return this;
    }

    @Override
    public Query setBoolean(int position, boolean val) {
        if (countQuery != null) {
            countQuery.setBoolean(position, val);
        }
        query.setBoolean(position, val);
        return this;
    }

    @Override
    public Query setBoolean(String name, boolean val) {
        if (countQuery != null) {
            countQuery.setBoolean(name, val);
        }
        query.setBoolean(name, val);
        return this;
    }

    @Override
    public Query setByte(int position, byte val) {
        if (countQuery != null) {
            countQuery.setByte(position, val);
        }
        query.setByte(position, val);
        return this;
    }

    @Override
    public Query setByte(String name, byte val) {
        if (countQuery != null) {
            countQuery.setByte(name, val);
        }
        query.setByte(name, val);
        return this;
    }

    @Override
    public Query setCacheable(boolean cacheable) {
        query.setCacheable(cacheable);
        return this;
    }

    @Override
    public Query setCacheMode(CacheMode cacheMode) {
        query.setCacheMode(cacheMode);
        return this;
    }

    @Override
    public Query setCacheRegion(String cacheRegion) {
        query.setCacheRegion(cacheRegion);
        return this;
    }

    @Override
    public Query setCalendar(int position, Calendar calendar) {
        if (countQuery != null) {
            countQuery.setCalendar(position, calendar);
        }
        query.setCalendar(position, calendar);
        return this;
    }

    @Override
    public Query setCalendar(String name, Calendar calendar) {
        if (countQuery != null) {
            countQuery.setCalendar(name, calendar);
        }
        query.setCalendar(name, calendar);
        return this;
    }

    @Override
    public Query setCalendarDate(int position, Calendar calendar) {
        if (countQuery != null) {
            countQuery.setCalendarDate(position, calendar);
        }
        query.setCalendarDate(position, calendar);
        return this;
    }

    @Override
    public Query setCalendarDate(String name, Calendar calendar) {
        if (countQuery != null) {
            countQuery.setCalendarDate(name, calendar);
        }
        query.setCalendarDate(name, calendar);
        return this;
    }

    @Override
    public Query setCharacter(int position, char val) {
        if (countQuery != null) {
            countQuery.setCharacter(position, val);
        }
        query.setCharacter(position, val);
        return this;
    }

    @Override
    public Query setCharacter(String name, char val) {
        if (countQuery != null) {
            countQuery.setCharacter(name, val);
        }
        query.setCharacter(name, val);
        return this;
    }

    @Override
    public Query setComment(String comment) {
        query.setComment(comment);
        return this;
    }

    @Override
    public Query setDate(int position, Date date) {
        if (countQuery != null) {
            countQuery.setDate(position, date);
        }
        query.setDate(position, date);
        return this;
    }

    @Override
    public Query setDate(String name, Date date) {
        if (countQuery != null) {
            countQuery.setDate(name, date);
        }
        query.setDate(name, date);
        return this;
    }

    @Override
    public Query setDouble(int position, double val) {
        if (countQuery != null) {
            countQuery.setDouble(position, val);
        }
        query.setDouble(position, val);
        return this;
    }

    @Override
    public Query setDouble(String name, double val) {
        if (countQuery != null) {
            countQuery.setDouble(name, val);
        }
        query.setDouble(name, val);
        return this;
    }

    @Override
    public Query setEntity(int position, Object val) {
        if (countQuery != null) {
            countQuery.setEntity(position, val);
        }
        query.setEntity(position, val);
        return this;
    }

    @Override
    public Query setEntity(String name, Object val) {
        if (countQuery != null) {
            countQuery.setEntity(name, val);
        }
        query.setEntity(name, val);
        return this;
    }

    @Override
    public Query setFetchSize(int fetchSize) {
        query.setFetchSize(fetchSize);
        return this;
    }

    @Override
    public Query setLockOptions(LockOptions lockOptions) {
        return countQuery;
    }

    @Override
    public Query setFirstResult(int firstResult) {
        query.setFirstResult(firstResult);
        return this;
    }

    @Override
    public boolean isReadOnly() {
        return true;
    }

    @Override
    public Query setFloat(int position, float val) {
        if (countQuery != null) {
            countQuery.setFloat(position, val);
        }
        query.setFloat(position, val);
        return this;
    }

    @Override
    public Query setFloat(String name, float val) {
        if (countQuery != null) {
            countQuery.setFloat(name, val);
        }
        query.setFloat(name, val);
        return this;
    }

    @Override
    public Query setFlushMode(FlushMode flushMode) {
        return query.setFlushMode(flushMode);
    }

    @Override
    public Query setInteger(int position, int val) {
        if (countQuery != null) {
            countQuery.setInteger(position, val);
        }
        query.setInteger(position, val);
        return this;
    }

    @Override
    public Query setInteger(String name, int val) {
        if (countQuery != null) {
            countQuery.setInteger(name, val);
        }
        query.setInteger(name, val);
        return this;
    }

    @Override
    public Query setLocale(int position, Locale locale) {
        if (countQuery != null) {
            countQuery.setLocale(position, locale);
        }
        query.setLocale(position, locale);
        return this;
    }

    @Override
    public Query setLocale(String name, Locale locale) {
        if (countQuery != null) {
            countQuery.setLocale(name, locale);
        }
        query.setLocale(name, locale);
        return this;
    }

    @Override
    public Query setLockMode(String alias, LockMode lockMode) {
        query.setLockMode(alias, lockMode);
        return this;
    }

    @Override
    public Query setLong(int position, long val) {
        if (countQuery != null) {
            countQuery.setLong(position, val);
        }
        query.setLong(position, val);
        return this;
    }

    @Override
    public Query setLong(String name, long val) {
        if (countQuery != null) {
            countQuery.setLong(name, val);
        }
        query.setLong(name, val);
        return this;
    }

    @Override
    public Query setMaxResults(int maxResults) {
        query.setMaxResults(maxResults);
        return this;
    }

    @Override
    public Query setParameter(int position, Object val, Type type) {
        if (countQuery != null) {
            countQuery.setParameter(position, val, type);
        }
        query.setParameter(position, val, type);
        return this;
    }

    @Override
    public Query setParameter(int position, Object val){
        if (countQuery != null) {
            countQuery.setParameter(position, val);
        }
        query.setParameter(position, val);
        return this;
    }

    @Override
    public Query setParameter(String name, Object val, Type type) {
        if (countQuery != null) {
            countQuery.setParameter(name, val, type);
        }
        query.setParameter(name, val, type);
        return this;
    }

    @Override
    public Query setParameter(String name, Object val){
        if (countQuery != null) {
            countQuery.setParameter(name, val);
        }
        query.setParameter(name, val);
        return this;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Query setParameterList(String name, Collection vals, Type type){
        if (countQuery != null) {
            countQuery.setParameterList(name, vals, type);
        }
        query.setParameterList(name, vals, type);
        return this;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Query setParameterList(String name, Collection vals){
        if (countQuery != null) {
            countQuery.setParameterList(name, vals);
        }
        query.setParameterList(name, vals);
        return this;
    }

    @Override
    public Query setParameterList(String name, Object[] vals, Type type){
        if (countQuery != null) {
            countQuery.setParameterList(name, vals, type);
        }
        query.setParameterList(name, vals, type);
        return this;
    }

    @Override
    public Query setParameterList(String name, Object[] vals){
        if (countQuery != null) {
            countQuery.setParameterList(name, vals);
        }
        query.setParameterList(name, vals);
        return this;
    }

    @Override
    public Query setParameters(Object[] values, Type[] types){
        if (countQuery != null) {
            countQuery.setParameters(values, types);
        }
        query.setParameters(values, types);
        return this;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Query setProperties(Map bean){
        if (countQuery != null) {
            countQuery.setProperties(bean);
        }
        query.setProperties(bean);
        return this;
    }

    @Override
    public Query setProperties(Object bean){
        if (countQuery != null) {
            countQuery.setProperties(bean);
        }
        query.setProperties(bean);
        return this;
    }

    @Override
    public Query setReadOnly(boolean readOnly) {
        query.setReadOnly(readOnly);
        return this;
    }

    @Override
    public Query setResultTransformer(ResultTransformer transformer) {
        query.setResultTransformer(transformer);
        return this;
    }

    @Override
    public Query setSerializable(int position, Serializable val) {
        query.setSerializable(position, val);
        return this;
    }

    @Override
    public Query setSerializable(String name, Serializable val) {
        query.setSerializable(name, val);
        return this;
    }

    @Override
    public Query setShort(int position, short val) {
        if (countQuery != null) {
            countQuery.setShort(position, val);
        }
        query.setShort(position, val);
        return this;
    }

    @Override
    public Query setShort(String name, short val) {
        if (countQuery != null) {
            countQuery.setShort(name, val);
        }
        query.setShort(name, val);
        return this;
    }

    @Override
    public Query setString(int position, String val) {
        if (countQuery != null) {
            countQuery.setString(position, val);
        }
        query.setString(position, val);
        return this;
    }

    @Override
    public Query setString(String name, String val) {
        if (countQuery != null) {
            countQuery.setString(name, val);
        }
        query.setString(name, val);
        return this;
    }

    @Override
    public Query setText(int position, String val) {
        if (countQuery != null) {
            countQuery.setText(position, val);
        }
        query.setText(position, val);
        return this;
    }

    @Override
    public Query setText(String name, String val) {
        if (countQuery != null) {
            countQuery.setText(name, val);
        }
        query.setText(name, val);
        return this;
    }

    @Override
    public Query setTime(int position, Date date) {
        if (countQuery != null) {
            countQuery.setTime(position, date);
        }
        query.setTime(position, date);
        return this;
    }

    @Override
    public Query setTime(String name, Date date) {
        if (countQuery != null) {
            countQuery.setTime(name, date);
        }
        query.setTime(name, date);
        return this;
    }

    @Override
    public Query setTimeout(int timeout) {
        if (countQuery != null) {
            countQuery.setTimeout(timeout);
        }
        query.setTimeout(timeout);
        return this;
    }

    @Override
    public Query setTimestamp(int position, Date date) {
        if (countQuery != null) {
            countQuery.setTimestamp(position, date);
        }
        query.setTimestamp(position, date);
        return this;
    }

    @Override
    public Query setTimestamp(String name, Date date) {
        if (countQuery != null) {
            countQuery.setTimestamp(name, date);
        }
        query.setTimestamp(name, date);
        return this;
    }

    @Override
    public Object uniqueResult(){
        query.uniqueResult();
        return this;
    }
}